File: /var/www/kevin-demo/wp-content/plugins/allspice/includes/membership-page.php
<?php
// includes/membership-page.php
//
// Native membership landing page. Newly generated pages carry the fully editable Gutenberg
// sales-page pattern (membership-blocks.php); the [allspice_membership] shortcode below is
// kept registered forever for pages generated by earlier plugin versions - those keep
// working untouched. Either way, authentication, products/prices, checkout, entitlement
// refresh and account management all stay in the widget (the buttons are the same
// data-allspice-action hooks / #allspice-* hashes every other membership surface uses).
if (!defined('ABSPATH')) exit;
const ALLSPICE_MEMBERSHIP_PAGE_OPTION = 'allspice_membership_page_id';
const ALLSPICE_CREATE_MEMBERSHIP_PAGE_ACTION = 'allspice_create_membership_page';
const ALLSPICE_REGENERATE_MEMBERSHIP_PAGE_ACTION = 'allspice_regenerate_membership_page';
function allspice_membership_page_slug(): string {
$slug = sanitize_title((string)apply_filters('allspice_membership_page_slug', 'membership'));
return $slug !== '' ? $slug : 'membership';
}
function allspice_membership_page_title(): string {
$title = trim((string)apply_filters('allspice_membership_page_title', 'Membership'));
return $title !== '' ? $title : 'Membership';
}
/* PHP twin of the widget's benefit labels (raw code title-cased as fallback). */
function allspice_membership_benefit_label(string $code): string {
$map = [
'ad_free' => 'Ad-free browsing',
'gated_recipes' => 'Members-only recipes',
'recipe_card' => 'Members-only recipe cards',
'meal_plans' => 'Member meal plans',
'members_only_content' => 'Members-only articles',
];
$code = trim($code);
if ($code === '') return '';
if (isset($map[$code])) return $map[$code];
return ucwords(str_replace(['_', '-'], ' ', $code));
}
/* ------------------------------------------------------------------------------ shortcode */
add_shortcode('allspice_membership', 'allspice_membership_shortcode');
function allspice_membership_shortcode(): string {
$m = function_exists('allspice_memberships_config') ? allspice_memberships_config() : [];
$tp = function_exists('allspice_theme_policies_cache_get') ? allspice_theme_policies_cache_get() : [];
$policy = isset($tp['policy']) && is_array($tp['policy']) ? $tp['policy'] : [];
$n = function_exists('allspice_memberships_normalized') ? allspice_memberships_normalized() : null;
$title = trim((string)($m['name'] ?? 'Membership'));
if ($title === '') $title = 'Membership';
$description = trim((string)($m['description'] ?? 'Support the site and unlock members-only content.'));
$join_label = $n !== null && $n['gate_ui']['join_label'] !== '' ? $n['gate_ui']['join_label'] : 'Become a member';
$login_label = $n !== null && $n['gate_ui']['login_label'] !== '' ? $n['gate_ui']['login_label'] : 'Already a member? Log in';
$signups_open = function_exists('allspice_memberships_new_signups_enabled')
? allspice_memberships_new_signups_enabled() : true;
$logo = trim((string)($policy['brand_logo_url'] ?? $policy['brand_logo_square_url'] ?? ''));
$html = '<div class="allspice-membership-page">';
if ($logo !== '' && preg_match('#^https?://#i', $logo)) {
$html .= '<div class="allspice-membership-page__brand"><img src="' . esc_url($logo) . '" alt="" /></div>';
}
$html .= '<h2 class="allspice-membership-page__title">' . esc_html($title) . '</h2>';
$html .= '<p class="allspice-membership-page__copy">' . esc_html($description) . '</p>';
/* Full normalized benefits list (built-in + custom, all of them - landing page shows
everything; only gates apply the compact limit). */
$html .= function_exists('allspice_membership_benefits_html') ? allspice_membership_benefits_html(0) : '';
$html .= '<div class="allspice-membership-page__actions">';
if ($signups_open) {
$html .= '<a class="allspice-recipe-gate__join" href="#allspice-membership" data-allspice-action="open_membership" role="button">' . esc_html($join_label) . '</a>';
} else {
/* Closed signups: no active Join - existing members still log in and manage. */
$html .= '<p class="allspice-recipe-gate__closed">' . esc_html(function_exists('allspice_membership_closed_signups_message') ? allspice_membership_closed_signups_message() : 'Membership is not currently accepting new signups.') . '</p>';
}
$html .= '<a class="allspice-recipe-gate__login" href="#allspice-login" data-allspice-action="open_login" role="button">' . esc_html($login_label) . '</a>'
. '</div>'
/* Manage is hidden in the INITIAL server HTML; the page bundle reveals it only for a
verified active member (safe GET summary). Hash link still works if revealed. */
. '<div class="allspice-membership-page__manage">'
. '<a href="#allspice-account" data-allspice-action="open_account" role="button" style="display:none">Manage membership</a>'
. '</div>';
return $html . '</div>';
}
/* Lightweight responsive styling for the landing wrapper, benefits and actions - rides the
always-loaded gate stylesheet handle; the page stays a normal editable shortcode page. */
/* Landing styles moved into allspice_gate_css() (membership-gate.php) so the in-body
optimizer-armor embed covers this page too. */
/* ------------------------------------------------------------------------ admin creation */
/*
* Content for a freshly generated page: the full editable Gutenberg sales-page pattern
* (membership-blocks.php). The bare shortcode remains only as a fallback for a WP too old
* to have blocks - and stays registered forever for pages that already carry it.
*/
function allspice_membership_page_default_content(): string {
if (function_exists('allspice_membership_sales_page_content') && function_exists('register_block_type')) {
return allspice_membership_sales_page_content();
}
return '[allspice_membership]';
}
/* The saved membership page when it is still a valid, non-trashed page - else null. */
function allspice_membership_page_current() {
$saved = (int)get_option(ALLSPICE_MEMBERSHIP_PAGE_OPTION, 0);
if ($saved <= 0) return null;
$post = get_post($saved);
if (!$post || $post->post_type !== 'page' || $post->post_status === 'trash') return null;
return $post;
}
function allspice_membership_page_editor_url(int $page_id): string {
return admin_url('post.php?post=' . $page_id . '&action=edit');
}
/* Legacy = shortcode-era generated page: contains [allspice_membership] and no block markup.
Such pages keep working untouched; settings offers Regenerate for an editable version. */
function allspice_membership_page_is_legacy_shortcode(string $content): bool {
if (strpos($content, '[allspice_membership]') === false) return false;
return strpos($content, '<!-- wp:') === false;
}
/*
* Create Membership Page - idempotent, never publishes, never overwrites publisher edits:
* 1. valid saved page id -> reuse
* 2. page at the slug -> adopt (store its id)
* 3. otherwise -> create a DRAFT page with the shortcode
* The stored id feeds allspice_membership_page_id, which the content-gate exclusion (and
* the allspice_membership_page_id filter) already honors - this page can never be gated.
*/
function allspice_create_membership_page(bool $force_create = false): array {
if (!$force_create) {
$current = allspice_membership_page_current();
if ($current !== null) {
return ['ok' => true, 'page_id' => (int)$current->ID, 'created' => false, 'reason' => 'reused_saved_id'];
}
$slug = allspice_membership_page_slug();
$by_slug = function_exists('get_page_by_path') ? get_page_by_path($slug, OBJECT, 'page') : null;
if ($by_slug && $by_slug->post_status !== 'trash') {
update_option(ALLSPICE_MEMBERSHIP_PAGE_OPTION, (int)$by_slug->ID, false);
return ['ok' => true, 'page_id' => (int)$by_slug->ID, 'created' => false, 'reason' => 'adopted_slug_page'];
}
}
$page_id = wp_insert_post([
'post_type' => 'page',
'post_status' => 'draft', // NEVER published automatically
'post_title' => allspice_membership_page_title(),
'post_name' => allspice_membership_page_slug(),
'post_content' => allspice_membership_page_default_content(),
], true);
if (is_wp_error($page_id) || (int)$page_id <= 0) {
return ['ok' => false, 'page_id' => 0, 'created' => false, 'reason' => 'insert_failed'];
}
update_option(ALLSPICE_MEMBERSHIP_PAGE_OPTION, (int)$page_id, false);
return ['ok' => true, 'page_id' => (int)$page_id, 'created' => true, 'reason' => 'created_draft'];
}
/*
* Regenerate: the CURRENT generated page goes to Trash (recoverable - publisher edits are
* never destroyed), then a fresh draft is created from the latest default pattern with
* current pricing. force_create skips reuse/adopt so the trashed page can't be re-adopted.
*/
function allspice_regenerate_membership_page(): array {
$current = allspice_membership_page_current();
if ($current !== null) {
wp_delete_post((int)$current->ID, false); /* false = Trash, NEVER permanent delete */
}
delete_option(ALLSPICE_MEMBERSHIP_PAGE_OPTION);
$r = allspice_create_membership_page(true);
$r['trashed_page_id'] = $current !== null ? (int)$current->ID : 0;
return $r;
}
add_action('admin_post_' . ALLSPICE_CREATE_MEMBERSHIP_PAGE_ACTION, 'allspice_admin_post_create_membership_page');
function allspice_admin_post_create_membership_page(): void {
if (!current_user_can('manage_options')) wp_die('Forbidden', 403);
check_admin_referer(ALLSPICE_CREATE_MEMBERSHIP_PAGE_ACTION);
$r = allspice_create_membership_page();
if (!empty($r['ok']) && (int)$r['page_id'] > 0) {
/* Success (created OR reused/adopted) lands the admin straight in the page editor. */
wp_safe_redirect(allspice_membership_page_editor_url((int)$r['page_id']));
exit;
}
set_transient('allspice_admin_notice', ['msg' => 'Could not create the membership page.', 'type' => 'error'], 30);
wp_safe_redirect(admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE));
exit;
}
add_action('admin_post_' . ALLSPICE_REGENERATE_MEMBERSHIP_PAGE_ACTION, 'allspice_admin_post_regenerate_membership_page');
function allspice_admin_post_regenerate_membership_page(): void {
if (!current_user_can('manage_options')) wp_die('Forbidden', 403);
check_admin_referer(ALLSPICE_REGENERATE_MEMBERSHIP_PAGE_ACTION);
$r = allspice_regenerate_membership_page();
if (!empty($r['ok']) && (int)$r['page_id'] > 0) {
wp_safe_redirect(allspice_membership_page_editor_url((int)$r['page_id']));
exit;
}
set_transient('allspice_admin_notice', ['msg' => 'Could not regenerate the membership page.', 'type' => 'error'], 30);
wp_safe_redirect(admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE));
exit;
}